/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 *
 */

import process from 'process';

import ts from 'typescript';

import { HvigorLogger } from '../../log/hvigor-log.js';
import { compilerOptions } from '../../util/options/compile-options.js';
import { formatTime } from '../../util/time-util.js';

const log = HvigorLogger.getLogger('ts-check');

export const hvigorfileTSRoots: Set<string> = new Set();
export const targetRoots: Set<string> = new Set();
export const targetModules: Set<string> = new Set();
export let hvigorConfigFileTsRoot: string | undefined;

const formatHost: ts.FormatDiagnosticsHost = {
  getCanonicalFileName: (path) => path,
  getCurrentDirectory: ts.sys.getCurrentDirectory,
  getNewLine: () => ts.sys.newLine,
};

/**
 * 以每个hvigorfile.ts和hvigorconfig.ts为入口文件并行进行类型检查
 *
 */
export function hvigorfileTypeCheck(): void {
  const startTime = process.hrtime();

  const program = ts.createProgram([...targetRoots], compilerOptions);
  const results = ts.getPreEmitDiagnostics(program);
  const taskEndTime: [number, number] = process.hrtime(startTime);
  const realTime = formatTime(taskEndTime);
  for (const result of results) {
    log.error(formatDiagnostic(result, formatHost));
  }
  if (results.length) {
    log.printErrorExit('TYPE_ERRORS', [[...targetRoots].join(', ')]);
  } else {
    log.info(`TYPE CHECK SUCCESSFUL in ${realTime}`);
  }
}

/**
 * compilePlugin 插件语法检查
 *
 * @param compilePluginPath compilePlugin文件绝对路径
 */
export function doCompilePluginTypeCheck(compilePluginPath: string) {
  const program = ts.createProgram([compilePluginPath], {
    ...compilerOptions,
    noImplicitAny: false,
    noImplicitReturns: false,
    alwaysStrict: false,
    declaration: false
  });
  const results = ts.getPreEmitDiagnostics(program);
  if (results.length) {
    for (const result of results) {
      log.error(formatDiagnostic(result, formatHost));
    }
    log.printErrorExit('TYPE_ERRORS', [compilePluginPath]);
  }
}

/**
 * 格式化诊断结果
 *
 * @param diagnostic 格式化后前的诊断结果
 * @param host 格式化host
 * @param fileName 指定的诊断结果对应的文件
 * @returns {string} 格式化后的诊断结果
 */
export function formatDiagnostic(diagnostic: ts.Diagnostic, host: ts.FormatDiagnosticsHost, fileName?: string): string {
  const errorMessage =
    `${diagnosticCategoryName(diagnostic)} TS${diagnostic.code}: ` +
    `${ts.flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`;

  if (diagnostic.file) {
    const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
    return `${errorMessage} at ${fileName ?? diagnostic.file.fileName}:${line + 1}:${character + 1} `;
  }

  return errorMessage;
}

/**
 * 返回错误类别
 *
 * @param d 诊断结果
 * @param lowerCase 是否转为小写
 * @returns {string} 错误类别
 */
function diagnosticCategoryName(d: { category: ts.DiagnosticCategory }, lowerCase = true): string {
  const name = ts.DiagnosticCategory[d.category];
  return lowerCase ? name.toLowerCase() : name;
}

export function setHvigorConfigFileTsRoot(root: string) {
  hvigorConfigFileTsRoot = root;
}
